package hello.word.m2.maven.jpa;

import org.apache.commons.lang3.StringUtils;

import javax.persistence.Column;
import java.awt.print.Book;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *
 */
public class PartTree {

    /*private static final String PREDICATE_PATTERN= Stream.of(PredicateEnum.values())
            .map(predicateEnum -> predicateEnum.getPredicate())
            .collect(Collectors.joining("|"));*/

    /*private static final String CONJUNCTION_PATTERN= Stream.of(ConjunctionEnum.values())
            .map(predicateEnum -> predicateEnum.getConjunction())
            .collect(Collectors.joining("|"));*/

    protected final String source;
    protected final Class<?> clazz;

    protected  ConjunctionEnum conjunction;
    protected PredicateEnum predicate;

    protected String columnName;

    protected List<PartTree> child;

    public PartTree(String source, Class<?> clazz) {
        this.source = source;
        this.clazz = clazz;

        String[] splitOr = source.split("Or");
        String[] splitAnd = source.split("And");
        //split by "or/and"
        if (splitOr.length>1){
            conjunction=ConjunctionEnum.Or;
            buildChild(splitOr);
        }else if(splitAnd.length >1){
            conjunction=ConjunctionEnum.And;
            buildChild(splitAnd);
        }else {
            // no conjunction exites , it is a predicate node
            predicate = this.getPredicate(source);
            int index = source.lastIndexOf(predicate.getPredicate());
            if (index == -1){
                index= source.length();
            }
            String filedName = source.substring(0,index);
            columnName = this.getColumnName(clazz,filedName)
                    .orElseThrow(()->new IllegalStateException("no such file "+filedName+" in class" +clazz));

        }


    }

    public static void main(String[] args) {
        String source = "firstName";
        PartTree p = new PartTree(source, Book.class);
        System.out.println(p.getSql());

        String source1 = "firstNameAndId";
        PartTree p1 = new PartTree(source1, Book.class);
        System.out.println(p1.getSql());

        String source2 = "idBetween";
        PartTree p2 = new PartTree(source2, Book.class);
        System.out.println(p2.getSql());

        String source3 = "firstNameIsNull";
        PartTree p3 = new PartTree(source3, Book.class);
        System.out.println(p3.getSql());

        String source4 = "firstNameLike";
        PartTree p4 = new PartTree(source4, Book.class);
        System.out.println(p4.getSql());
    }

    private PredicateEnum getPredicate(String source){
        return  Stream.of(PredicateEnum.values()).filter(predicate -> {
            Pattern p = Pattern.compile("("+predicate.getPredicate()+")$");
            Matcher matcher = p.matcher(source);
            return matcher.find();
        }).findFirst().orElse(PredicateEnum.Is);
    }

    private Optional<String> getColumnName(Class<?> entityClass, String filedName)  {
//		entityClass.getDeclaredFields()[0].
        return Stream.of(entityClass.getDeclaredFields())
                .filter(field -> {
                    //upcase first char

                    char chars = filedName.charAt(0);
                    if (chars >'A' && chars <'Z'){
                        chars +=32;
                    }
                    StringBuilder sb = new StringBuilder(filedName);
                    sb.setCharAt(0,chars);
                    return StringUtils.equals(sb.toString(),field.getName());
                })
                .map(field -> field.getAnnotation(Column.class))
                .map(Column::name).findFirst();

    }

     public String getSql(){
        if(null != conjunction){
            return child.stream().map(c-> c.getSql())
                    .collect(Collectors.joining(conjunction.getExpression()));
        }else{
            return columnName+predicate.getExpression();
        }
     }

    public void buildChild(String[] split){
        this.child=Stream.of(split)
                .map(s -> new PartTree(s,clazz))
                .collect(Collectors.toList());
    }
}
